#ifndef I_LIB_IP_I_RESIZE_H
#define I_LIB_IP_I_RESIZE_H

#ifndef I_PLATFORM_NO_SSE_SUPPORT
#include <emmintrin.h>
#endif

#include "../core/i_blas.h"

namespace idl
{
    /*special operation to fill tails when doing 1/2 downsample*/
    inline float i_half_lum32f_sample_tail(int dst_x, int dst_y, const float *src, int src_width)
    {
        int src_x = (dst_x << 1);
        int src_y = (dst_y << 1);
        const float *cp_src = src + (src_y*src_width + src_x);
        return (cp_src[0] + cp_src[1] + cp_src[src_width] + cp_src[src_width+1]) / 4;  
    }

    /*half (1/2) downsample an image by copy the nearest-neighbor pixel*/
    template<typename T>
    inline void i_half_lum_nn(const T* src, T *dst, int width, int height)
    {
        int row, col;
        int width_dst = width >> 1;
        int height_dst = height >> 1;
        int two_width = width << 1;
        T *row_dst = dst;
        const T *row_src = src;
        for (row = 0; row < height_dst; ++row)
        {
            for (col = 0; col < width_dst; ++col)
            {
                row_dst[col] = row_src[(col << 1)];
            } 
            row_dst += width_dst;
            row_src += two_width;
        }
    }

    /*half (1/2) downsample an image by averaging every 4 pixels*/
    template<typename T>
    inline void i_half_lum(const T* src, T *dst, int width, int height)
    {
        int row;
        int width_dst = width >> 1;
        int height_dst = height >> 1;
        int two_width = width << 1;
        T *row_dst = dst;
        const T *row_src = src;
        T* pdst = NULL;
        const T* psrc_1 = NULL;
        const T* psrc_2 = NULL;
        for (row = 0; row < height_dst; ++row)
        {
            psrc_1 = row_src;
            psrc_2 = row_src + width;
            for (pdst = row_dst; pdst < row_dst + width_dst; ++pdst)
            {
                *pdst = (psrc_1[0] + psrc_1[1] + psrc_2[0] + psrc_2[1]) / 4;
                psrc_1 += 2;
                psrc_2 += 2;
            }
            row_dst += width_dst;
            row_src += two_width;
        }
    }

    template<typename T>
    inline void i_half_lum_w_max(const T* src, T *dst, int width, int height)
    {
        int row;
        int width_dst = width >> 1;
        int height_dst = height >> 1;
        int two_width = width << 1;
        T *row_dst = dst;
        const T *row_src = src;
        T* pdst = NULL;
        const T* psrc_1 = NULL;
        const T* psrc_2 = NULL;
        for (row = 0; row < height_dst; ++row)
        {
            psrc_1 = row_src;
            psrc_2 = row_src + width;
            for (pdst = row_dst; pdst < row_dst + width_dst; ++pdst)
            {
                *pdst = i_max(i_max(psrc_1[0], psrc_1[1]), i_max(psrc_2[0], psrc_2[1]));
                psrc_1 += 2;
                psrc_2 += 2;
            }
            row_dst += width_dst;
            row_src += two_width;
        }
    }

  /*double (x2) upsample an image*/
  /*template<typename T>
  inline void i_double_lum(const T* src, T *dst, int width, int height)
  {
    int x, y;
    int width_dst = width << 1;
    int height_dst = height << 1;
    int two_width = width << 1;
  }*/
  
#ifndef I_PLATFORM_NO_SSE_SUPPORT
    /*half(1/2) downsample a float image, using SSE2 acceleration*/
    inline void i_half_lum32f_sse(const float* src, float *dst, int width, int height)
    {
        int x, y;
        int width_dst  = width >> 1;
        int height_dst = height >> 1;
        int two_width = width << 1;
        int nr_loop   = width_dst >> 2;
        int remainder = width_dst - nr_loop*4;
        int offset_s = 0, offset_d = 0;
        register __m128 a, b, c, d, r, nm;
        float *p_dst = NULL;
        const float *cp_src_a = NULL;
        const float *cp_src_c = NULL;
        nm = _mm_set_ps(0.25f, 0.25f, 0.25f, 0.25f);
        for (y = 0; y < height_dst; ++y, offset_s += two_width, offset_d += width_dst)
        {
            cp_src_a = src + offset_s;
            cp_src_c = cp_src_a + width;
            p_dst = dst + offset_d;
            for (x = 0; x < nr_loop; ++x, p_dst += 4, cp_src_a += 8, cp_src_c += 8)
            {
                a = _mm_setr_ps(cp_src_a[0], cp_src_a[2], cp_src_a[4], cp_src_a[6]);
                b = _mm_setr_ps(cp_src_a[1], cp_src_a[3], cp_src_a[5], cp_src_a[7]);
                c = _mm_setr_ps(cp_src_c[0], cp_src_c[2], cp_src_c[4], cp_src_c[6]);
                d = _mm_setr_ps(cp_src_c[1], cp_src_c[3], cp_src_c[5], cp_src_c[7]);
                
                r = _mm_add_ps(a, b);
                r = _mm_add_ps(r, c);
                r = _mm_add_ps(r, d);  //r = a+b+c+d
                r = _mm_mul_ps(r, nm); //normalize r = r/4 
                _mm_storeu_ps(p_dst, r); //not necessarily 16-byte aligned     
            }
            /*fills the remainder:*/
            switch (remainder)
            {
            case 0: break;
            case 1: p_dst[0] = i_half_lum32f_sample_tail(width_dst - 1, y, src, width);
                break;
            case 2: p_dst[0] = i_half_lum32f_sample_tail(width_dst - 2, y, src, width);
                p_dst[1] = i_half_lum32f_sample_tail(width_dst - 1, y, src, width);
                break;
            case 3: p_dst[0] = i_half_lum32f_sample_tail(width_dst - 3, y, src, width);
                p_dst[1] = i_half_lum32f_sample_tail(width_dst - 2, y, src, width);
                p_dst[2] = i_half_lum32f_sample_tail(width_dst - 1, y, src, width);
                break;
            default:break;
            }
        }
    }
  
 /*two-third(2/3) downsample a float image, using SSE2 acceleration
 | a b c |
 | d e f | ---> |s1 s2|
 | g h i | ---> |s3 s4|
 s1 = (a+b+d+e) / 4;
 s2 = (b+c+e+f) / 4;
 s3 = (d+e+g+h) / 4;
 s4 = (e+f+h+i) / 4;
    a  b  d  f 
    b  c  h  h
    d  f  g  i 
 +  e  e  e  e
 _________________
   (s1 s2 s3 s4)*4
 */
    inline void i_twothird_lum32f_sse(const float* src, float *dst, int width, int height)
    {
        int x, y;
        int nr_loop_hor = (width / 3);
        int nr_loop_ver = (height / 3);
        int width_dst  =  (nr_loop_hor << 1); //must be an even number
        //int height_dst =  (nr_loop_ver << 1); //must be an even number
        int two_width_dst = (width_dst << 1);
        int three_width = width*3;
        int offset_s = 0, offset_d = 0;
        register __m128 a, b, c, d, r, nm;
        float p_tmp[4];
        
        float *p_dst = NULL;
        const float *cp_src_a = NULL;
        const float *cp_src_d = NULL;
        const float *cp_src_g = NULL;
        
        nr_loop_hor -= 1;
        nm = _mm_set_ps(0.25f, 0.25f, 0.25f, 0.25f);
        for (y = 0; y < nr_loop_ver; ++y, offset_s += three_width, offset_d += two_width_dst)
        {
            cp_src_a = src + offset_s;
            cp_src_d = cp_src_a + width;
            cp_src_g = cp_src_d + width;
            p_dst = dst + offset_d;
            for (x = 0; 
                x < nr_loop_hor; 
                ++x, p_dst += 2, cp_src_a += 3, cp_src_d += 3, cp_src_g += 3)
            {
                a = _mm_setr_ps(cp_src_a[0], cp_src_a[1], cp_src_d[0], cp_src_d[2]);
                b = _mm_setr_ps(cp_src_a[1], cp_src_a[2], cp_src_g[1], cp_src_g[1]);
                c = _mm_setr_ps(cp_src_d[0], cp_src_d[2], cp_src_g[0], cp_src_g[2]);
                d = _mm_set_ps1(cp_src_d[1]);
                r = _mm_add_ps(a, b);
                r = _mm_add_ps(r, c);
                r = _mm_add_ps(r, d);  //r = a+b+c+d
                r = _mm_mul_ps(r, nm); //normalize r = r/4 
                _mm_storeu_ps(p_dst, r); //not necessarily 16-byte aligned
                i_copy2(p_dst + 2, p_dst + width_dst);
            }
            //fills the last reminder:
            a = _mm_setr_ps(cp_src_a[0], cp_src_a[1], cp_src_d[0], cp_src_d[2]);
            b = _mm_setr_ps(cp_src_a[1], cp_src_a[2], cp_src_g[1], cp_src_g[1]);
            c = _mm_setr_ps(cp_src_d[0], cp_src_d[2], cp_src_g[0], cp_src_g[2]);
            d = _mm_set_ps1(cp_src_d[1]);
            
            r = _mm_add_ps(a, b);
            r = _mm_add_ps(r, c);
            r = _mm_add_ps(r, d);  //r = a+b+c+d
            r = _mm_mul_ps(r, nm); //normalize r = r/4 
            _mm_storeu_ps(p_tmp, r); //not necessarily 16-byte aligned
            i_copy2(p_tmp, p_dst);
            i_copy2(p_tmp+2, p_dst+width_dst);
        }
    }
#endif
} /* namespace idl */

#endif